using System;
using Microsoft.SPOT;
using MFWebSocket;
using System.Text;
using SHA1LIB;

namespace MFWebSocket
{
    /// <summary>
    /// Tools Common to this solution and it's libraries
    /// Contains code from various authors
    /// </summary>
    public class Tools
    {
        #region Common

        /// <summary>
        /// All of this region was Authored by Julius Friedman
        /// </summary>
        
        ///Used by WebSocketClient.GenSecKey function when there is no Regex support to replace extra characters from the security string before converting it to a Int64
        //internal static void ReplaceExtraChars(ref StringBuilder sb)
        //{
        //    //sb.Replace("\0", string.Empty);
        //    for (int i = 33; i <= 126; ++i)
        //    {
        //        char currentChar = (char)i;
        //        if (currentChar >= '0' && currentChar <= '9') continue;
        //        else sb.Replace(new string(currentChar, 1), string.Empty);
        //    }
        //}

        /// <summary/>
        /// <param name="o"></param>
        /// <returns></returns>
        internal static byte __BYTE_UNBOX__(object o)
        {
            //a byte in a box 
            object r = o;
            if (r is byte) return (byte)r;
            else if (r is int) return (byte)(int)r;
            else if (r is char) return (byte)(char)r;
            else throw new Exception("Cannot unbox to byte");
        }

        static Random m_Random = new Random();
        
        /// <summary>
        /// Gets a random number
        /// </summary>
        /// <param name="max">The maxima</param>
        /// <returns>x | x &lt;=max</returns>
        internal static int Random(int max)
        {
            return m_Random.Next(max);
        }

        /// <summary>
        /// Random with min an max support
        /// </summary>
        /// <param name="min">minima</param>
        /// <param name="max">maxima</param>
        /// <returns>x|x&lt;=min && x&gt;= max</returns>
        internal static int Random(int min, int max)
        {
            if (min == max) return min;
            int result;
            do
                result = m_Random.Next(max);
            while (result < min);
            return result;
        }

        /// <summary>
        /// Counts the characters in a string
        /// </summary>
        /// <param name="s">string</param>
        /// <param name="c">toCount</param>
        /// <returns>The count of c in s</returns>
        internal static int CountChars(ref string s, ref char c)
        {
            int pos = 0, count = 0;
            while (!((pos = s.IndexOf(c, pos)) == -1))
            {
                //We have an occurance of the character increment position and count
                ++count; ++pos;
            }
            return count;
        }

        /// <summary>
        /// Matches a byte array to another byte array
        /// forwards or reverse
        /// </summary>
        /// <param name="a">byte array</param>
        /// <param name="offset">start offset</param>
        /// <param name="len">max length</param>
        /// <param name="b">byte array</param>
        /// <param name="direction">to move each iteration</param>
        /// <returns>true if all bytes match, otherwise false</returns>
        internal static bool Matches(byte[] a, int offset, int len, byte[] b, int direction = 1)
        {
            #region Only Matched from offset Within a and b, could not differ, e.g. if you wanted to mach in reverse for only part of a in some of b that would not work
            //if (direction == 0) throw new ArgumentException("direction");
            //for (; offset < len; offset += direction) if (a[offset] != b[offset]) return false;
            //return true;
            #endregion
            //Will match if b contains len of a and return a a index of positive value
            //return IndexOfBytes(a, offset, len, len, b) != -1;
            return IndexOfBytes(a, offset, len, b, len) != -1;
        }

        /// <summary>
        /// Swaps two integers without using a temporary variable
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        internal static void Swap(ref int a, ref int b, bool inplace = true)
        {
            if (inplace)
            {
                a ^= b;
                b ^= a;
                a ^= b;
            }
            else
            {
                int temp = a;
                a = b;
                b = temp;
            }
        }

        /// <summary>
        /// Swaps two bytes without using a temporary variable
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        internal static void Swap(ref byte a, ref byte b, bool inplace = true)
        {
            if (inplace)
            {
                a ^= b;
                b ^= a;
                a ^= b;
            }
            else
            {
                byte temp = a;
                a = b;
                b = temp;
            }
        }

        /////// <summary>
        /////// Can be used to find if a array starts, ends spot Matches or compltely contains a sub byte array
        /////// Set checkLength to the amount of bytes from the needle you want to match, start at 0 for forward searches start at hayStack.Length -1 for reverse matches
        /////// </summary>
        /////// <param name="a">Needle</param>
        /////// <param name="offset">Start in Haystack</param>
        /////// <param name="len">Length of required match</param>
        /////// <param name="b">Haystack</param>
        /////// <param name="direction">Which way to move the iterator</param>
        /////// <returns>Index if found, otherwise -1</returns>
        ////internal static int IndexOfBytes(byte[] needle, int offsetStart, int offsetEnd, int checkLength, byte[] haystack, int direction = 1)
        ////{
        ////    //If there is nothing to check we can return
        ////    if (checkLength <= 0) return -1;
        ////    //If the direction is == 0 we would spin forever making no progress
        ////    if (direction == 0) throw new ArgumentException("direction");
        ////    //Cache the length of the needle and the haystack, setup the endIndex for a reverse search
        ////    int needleLength = needle.Length, haystackLength = haystack.Length;
        ////    //Allocate a value for the endIndex and workingOffset
        ////    //If we are going forward then the bound is the haystackLength
        ////    if (direction >= 1 && offsetStart > offsetEnd) throw new ArgumentOutOfRangeException();
        ////    else if (direction < 0) Swap(ref offsetStart, ref offsetEnd);
        ////    //Cache of the checkIndex, set to the workingOffset in the FOR
        ////    int checkIndex = offsetStart;
        //////Label removes while
        ////Start:
        ////    if (checkIndex < offsetEnd) return -1;//We did not have a match with the given options if the checkIndex > endIndex
        ////    //FOR the (reset the checkIndex to workingOffset) to the checkLength compare the needle to the haystack if we do not have a match BREAK else CONTINUE
        ////    else for (checkIndex = offsetStart; checkIndex < checkLength; ++checkIndex) if (needle[checkIndex] != haystack[offsetStart + checkIndex]) break; else continue;
        ////    //If we have matched a binary pattern with the given options then return
        ////    if (checkIndex == checkLength) return offsetStart; //We are done matching
        ////    //Move the offset by the direction, reset the checkIndex to the workingOffset
        ////    else offsetStart += direction; goto Start;
        ////}

        /// <summary>
        /// Can be used to find if a array starts, ends spot Matches or compltely contains a sub byte array
        /// Set checkLength to the amount of bytes from the needle you want to match, start at 0 for forward searches start at hayStack.Lenght -1 for reverse matches
        /// </summary>
        /// <param name="a">Needle</param>
        /// <param name="offset">Start in Haystack</param>
        /// <param name="len">Length of required match</param>
        /// <param name="b">Haystack</param>
        /// <param name="direction">Which way to move the iterator</param>
        /// <returns>Index if found, otherwise -1</returns>
        internal static int IndexOfBytes(byte[] needle, int offset, int checkLength, byte[] haystack, int direction = 1)
        {
            //Ensure we are not still
            if (direction == 0) throw new ArgumentException("direction");
            //we will have an indirect movement value until there is atleast one match
            int inDirection = direction > 0 ? 1 : -1, realDirection = direction,
            //Determine where to end
            matchEndIndex = direction >= 1 ? 0 : needle.Length - 1,
            //Keep the count and Absolute direction so we can calculate when to stop
            matchCount = 0, absDirection = System.Math.Abs(direction);
            //Make the direction indirect
            direction = inDirection;

            //While the tensor keeps the bounds
            for (; matchEndIndex <= checkLength; offset += direction)
                if (needle[matchEndIndex] == haystack[offset])
                {
                    direction = realDirection;
                    matchEndIndex += direction;
                    ++matchCount;
                    if (matchCount == checkLength || absDirection * matchCount == checkLength) break;
                }
                else if (direction != inDirection)
                {
                    //Back to regular matching
                    direction = inDirection;
                }
            //Return where the match occured
            int result = offset - checkLength + direction;
            //Fix the index for reversed direction
            if (direction < 0) result += ++checkLength;
            return result;
        }

        /// <summary>
        /// Combines the given byte arrays
        /// </summary>
        /// <param name="bytez">The byte arrays to combine</param>
        /// <returns>The combination of the bytes or null if bytez is null</returns>
        internal static byte[] CombineArrays(params byte[][] bytez)
        {
            if (null == bytez) return null;
            return CombineArrays(bytez, 0, bytez.Length);
        }

        /// <summary>
        /// Combines the Given Arrays in any order from any offset and with certain length
        /// </summary>
        /// <param name="bytez">An array of byte[] to combine</param>        
        /// <param name="offset">The start offset</param>
        /// <param name="len">The length</param>
        /// <param name="direction">The direction</param>
        /// <returns>The combination of the bytes</returns>
        internal static byte[] CombineArrays(byte[][] bytez, int offset, int len, int direction = 1)
        {
            if (direction == 0) throw new ArgumentException("direction");
            if (bytez == null) return null;
            if (bytez.Length == 0) return emptyBytes;
            int end = bytez.Length;
            //Reverse
            if (direction < 0) Swap(ref offset, ref end);
            byte[] result = emptyBytes;
            for (; offset < end; offset += direction)
            {
                //Determine the next index because all of our calculations revolve only around that byte
                int nextIndex = offset + direction;
                //Combine the bytes if there is a next space otherwise combine it with emptyBytes
                result = Microsoft.SPOT.Hardware.Utility.CombineArrays(result, Microsoft.SPOT.Hardware.Utility.CombineArrays(bytez[offset], nextIndex > end ? bytez[offset + nextIndex] : emptyBytes));
            }
            return result;
        }

        /// <summary>
        /// Empty byte array used with ComineArrays
        /// </summary>
        internal static readonly byte[] emptyBytes = new byte[0];

        /// <summary>
        /// Reverse a byte array
        /// </summary>
        /// <param name="array">The byte[] to reverse</param>
        internal static void Reverse(byte[] array)
        {            
            Reverse(array, 0, array.Length);
        }
        
        /// <summary>
        /// Reverses a byte array 
        /// </summary>
        /// <param name="array">The bytes</param>
        /// <param name="index">The index in the bytes</param>
        /// <param name="len">The length of bytes to reverse</param>
        internal static void Reverse(byte[] array, int index, int len)
        {
            //O(n/2+1) 
            //Length = 5 => {0,4}, {1,3}, ? {2,2} never hit because !(near < far)
            ///-----Inplace swap modification-----
            ///If using inplace swap use the allocate outside the for loop
            ///byte temp;
            for (int near = index, far = len - 1; near < far; ++near, --far) Swap(ref array[near], ref array[far]);
            //{                
            //    #region Inplace swap
            //    //temp = array[near];
            //    //array[near] = array[far];
            //    //array[far] = temp;
            //    #endregion
            //}
        }

        /// <summary>
        /// Standard excpetion logging during debugging. Puts the method followed by the exception message and the stack trace in the Output window
        /// </summary>
        /// <param name="ex">The exception to log</param>
        internal static void LogException(string method, Exception ex)
        {
            Debug.Print(method + ex.Message + "\r\n");
            Debug.Print(ex.StackTrace);
        }

        #endregion

        #region Uri

        internal class UriFormatException : Exception
        {
            public UriFormatException(string msg) : base(msg) { }
        }
        
        public static char[] HexUpperChars = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
        public static char[] HexLowerChars = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
        
        //Origional Function From http://www.jexamples.com/vSrc/2859/org.apache.catalina.util.URLEncoder?prodId=tomcat&lineNo=68&implExtId=146744&queryText=java.lang.Exception.printStackTrace&qType=clsMeth
        //Apache 2 License       
        
        /// <summary>
        /// The amount of difference between lowercase and capital letters
        /// </summary>
        const int caseDiff = ('a' - 'A');

        public static string EscapeUriString(string stringToEscape)
        {
            try
            {
                if (stringToEscape == null)
                {
                    throw new ArgumentNullException("stringToUnescape");
                }
                if (stringToEscape.Length == 0)
                {
                    return string.Empty;
                }
                if ((stringToEscape.Length) >= 0xfff0)
                {
                    throw new UriFormatException("String Too Long");
                }
                int stringLen = stringToEscape.Length;
                StringBuilder sbResult = new StringBuilder(stringLen);
                // convert to external encoding before hex conversion
                byte[] bytes = UTF8Encoding.UTF8.GetBytes(stringToEscape);
                for (int i = 0, e = stringLen; i < e; ++i)
                {
                    //If the char is safe and not reserved append it to the result
                    char c = (char)bytes[i];
                    if (safeCharacters[(int)c]) sbResult.Append(c);
                    else
                    {
                        //Determine the escaped value from the UTF8 Encoding
                        byte toEncode = bytes[i];
                        int low = (int)(toEncode & 0x0f);
                        int high = (int)((toEncode & 0xf0) >> 4);
                        //Append the escaped values
                        sbResult.Append('%');
                        sbResult.Append(HexUpperChars[high]);
                        sbResult.Append(HexUpperChars[low]);
                    }
                }
                return sbResult.ToString();
            }
            catch
            {
                throw;
            }
        }

        /// <summary>
        /// Characters which are safe to be utilized in a Uri
        /// </summary>
        internal static BitArray safeCharacters = new BitArray(256);

        /// <summary>
        /// The constructor of the Tools class
        /// </summary>
        static Tools()
        {
            ///Add 'a' => 'z' by utilzing case diff
            for (char i = 'A'; i <= 'Z'; ++i)
            {
                safeCharacters.Set(i, true);
                safeCharacters.Set(i + caseDiff, true);
            }
            ///Add '0' => '9'
            for (char i = '0'; i <= '9'; ++i) safeCharacters.Set(i, true);
        }

        /// <summary>
        /// Allows developers to add characters to the array of characters which are safe and do note require encoding in a Uri.
        /// </summary>
        /// <param name="c"></param>
        public static void AddSafeCharacter(char c) { safeCharacters.Set(c, true); }
        
        #endregion

        #region Hex

        /// <summary>
        /// This region came from the netmf forums... SHA1 Contribution link to follow
        /// http://www.netmf.com/Discussion/Forums/SingleForum/SingleThread.aspx?mode=singleThread&thread=749469d7-55f0-425b-a03c-dca26e235cf7
        /// </summary>

        const string HEX_INDEX = "0123456789abcdef          ABCDEF";

        /// <summary>
        /// Gets the string representation of the given bytes
        /// </summary>
        /// <param name="bytes">The btyes</param>
        /// <returns>A string of the given bytes</returns>
        public static string GetString(byte[] bytes)
        {
            return GetString(bytes, 0, bytes.Length);
        }

        /// <summary>
        /// Gets the string representation of the given bytes
        /// </summary>
        /// <param name="bytes">The bytes</param>
        /// <param name="offset">The start offset</param>
        /// <param name="length">The length</param>
        /// <returns>A string of the given bytes</returns>
        public static string GetString(byte[] bytes, int offset, int length)
        {
            string s = "";

            for (int i = offset; i < length && i < bytes.Length; i++)
                s += (char)bytes[i];

            return s;
        }

        /// <summary>
        /// Gets a string of the hex representation of the given bytes
        /// </summary>
        /// <param name="b">The bytes</param>
        /// <returns>A string reprsenting the given bytes in hex</returns>
        public static string BytesToHex(byte[] b)
        {
            string res = "";

            for (int i = 0; i < b.Length; ++i)
                res += ByteToHex(b[i]);

            return res;
        }

        /// <summary>
        /// Gets the hex representation of the single given byte
        /// </summary>
        /// <param name="b">The byte</param>
        /// <returns>The string representation of the given byte</returns>
        public static string ByteToHex(byte b)
        {
            int lowByte = b & 0x0F;
            int highByte = (b & 0xF0) >> 4;

            return new string(
                new char[] { HexUpperChars[highByte], HexUpperChars[lowByte] }
            );
        }

        /// <summary>
        /// Converts the given string into it's byte representation
        /// </summary>
        /// <param name="s">The string</param>
        /// <returns>The byte representation of the given string</returns>
        public static byte[] HexToByte(string s)
        {
            int l = s.Length / 2;
            byte[] data = new byte[l];
            int j = 0;

            for (int i = 0; i < l; ++i)
            {
                char c = s[j++];
                int n, b;

                n = HEX_INDEX.IndexOf(c);
                b = (n & 0xf) << 4;
                c = s[j++];
                n = HEX_INDEX.IndexOf(c);
                b += (n & 0xf);
                data[i] = (byte)b;
            }

            return data;
        }

        /// <summary>
        /// A simplified version of Hex function.
        /// </summary>
        /// <param name="Number">
        /// Receives a Byte in decimal form.
        /// </param>
        /// <returns>
        /// Returns a hex string representing the conversion of a given byte in hexadecimal.
        /// </returns>
        public static string Hex(Byte Number)
        {
            int H1 = Number / SHA1.h2;
            int H2 = Number % SHA1.h2;
            String result = @"";
            if (H1 < 10)
            {
                result += (char)(H1 + SHA1.h6);
            }
            else
            {
                switch (H1)
                {
                    case 10:
                        result += @"A";
                        break;
                    case 11:
                        result += @"B";
                        break;
                    case 12:
                        result += @"C";
                        break;
                    case 13:
                        result += @"D";
                        break;
                    case 14:
                        result += @"E";
                        break;
                    case 15:
                        result += @"F";
                        break;
                }
            }
            if (H2 < 10)
            {
                result += (char)(H2 + SHA1.h6);
            }
            else
            {
                switch (H2)
                {
                    case 10:
                        result += @"A";
                        break;
                    case 11:
                        result += @"B";
                        break;
                    case 12:
                        result += @"C";
                        break;
                    case 13:
                        result += @"D";
                        break;
                    case 14:
                        result += @"E";
                        break;
                    case 15:
                        result += @"F";
                        break;
                }
            }
            return result;
        }

        /// <summary>
        /// A simplified version of Hex function.
        /// </summary>
        /// <param name="Number">
        /// Receives a unsigned 32-bits integer in decimal form.
        /// </param>
        /// <returns>
        /// Returns a hex string representing the conversion of a given unsigned 32-bits integer in hexadecimal.
        /// </returns>
        public static string Hex(UInt32 Number)
        {
            string result = @"";
            int i = SHA1.z;
            while (Number != 0)
            {
                if (Number % SHA1.h2 < 10)
                {
                    result += (char)(Number % SHA1.h2 + SHA1.h6);
                }
                else
                {
                    switch (Number % SHA1.h2)
                    {
                        case 10:
                            result += @"A";
                            break;
                        case 11:
                            result += @"B";
                            break;
                        case 12:
                            result += @"C";
                            break;
                        case 13:
                            result += @"D";
                            break;
                        case 14:
                            result += @"E";
                            break;
                        case 15:
                            result += @"F";
                            break;
                    }
                }
                Number /= SHA1.h2;
                ++i;
            }
            while (i < SHA1.h)
            {
                result += @"0";
                ++i;
            }
            String rresult = @"";
            for (i = 7; i >= SHA1.z; --i)
            {
                rresult += result[i];
            }
            return rresult;
        }

        /// <summary>
        /// Converts a ASCII chars array into a bitfield
        /// </summary>
        /// <param name="message">
        /// ASCII chars array
        /// </param>
        /// <returns>
        /// A bitfield containing the given ASCII chars array
        /// </returns>
        public static BitArray ConvertToBitArray(ref String message)
        {
            //int messageLength = message.Length;
            //Byte[] bytes = new Byte[messageLength];
            //for (int i = 0; i < messageLength; ++i) bytes[i] = (Byte)message[i];

            BitArray bitArray = new BitArray(System.Text.Encoding.UTF8.GetBytes(message));//bytes
            bool temp;
            //Try to make this as Fast as possible
            for (int i = SHA1.z, nextI = 1, E = message.Length; i < E; ++i, nextI = i + SHA1.c) for (int j = SHA1.z, swapA = SHA1.h * nextI - j - SHA1.c, swapB = SHA1.h * i + j; j < SHA1.g; ++j, swapA = SHA1.h * nextI - j - SHA1.c)
                {                    
                    temp = bitArray[swapB];
                    bitArray[swapB] = bitArray[swapA];
                    bitArray[swapA] = temp;
                }
            return bitArray;
        }

        /// <summary>
        /// The Space Character
        /// </summary>
        internal static char Space = ' ';

        /// <summary>
        /// Transforms a message digest string into a pretty print message digest.
        /// </summary>
        /// <param name="message">
        /// A hex digit message digest  string.
        /// </param>
        /// <returns>
        /// A hex digit message digest string with spaces between words.
        /// </returns>
        public static String PrettyPrint(String message)
        {
            return string.Concat(message.Substring(SHA1.z, SHA1.h), Space, message.Substring(SHA1.h, SHA1.h), Space, message.Substring(SHA1.h2, SHA1.h), Space, message.Substring(SHA1.h3, SHA1.h), Space, message.Substring(SHA1.h4, SHA1.h));
        }

        #endregion
    }
}
